#include "StatPack.h"
#include <sstream>

#define MAX_TIME 45000
#define TOTAL_TICKETS  1410
#define TARGET_MAX_WAIT_TIME  60
#define FLOOR_COUNT 5


StatPack::StatPack(void) : the95PercentileNumber(1410*0.95), targetWaitTime(60)
{

}

StatPack::StatPack(int elevCount, string fileName, bool flag): the95PercentileNumber(1410*0.95), targetWaitTime(60), runMinWait(MAX_TIME), runMaxWait(0),
	meanWaitTime(0.0), the60SecCount(0.0), percentileWaitTime(0.0), eCount(elevCount), baseFileName(fileName), writeFlag(flag)
{
	flrIntArray flrStatsInt;
	flrDblArray flrStatsDbl;
	flrStatsInt.fill(0);
	flrStatsDbl.fill(1.0);
	for (int count = 0; count < eCount; count++)
	{
		ElstatsCount.push_back(flrStatsInt);
		ElstatsWait.push_back(flrStatsDbl);
		ElstatsWaitMax.push_back(flrStatsInt);
		avgElstatsWait.push_back(flrStatsDbl);
		avgElstatsWaitMax.push_back(flrStatsInt);
		avgElstatsCount.push_back(flrStatsDbl);
		avgElstatsCountMax.push_back(flrStatsInt);

	}
	flrStatsInt.fill(MAX_TIME);
	for (int count = 0; count <eCount; count++)
	{
		ElstatsWaitMin.push_back(flrStatsInt);
		avgElstatsWaitMin.push_back(flrStatsInt);
		avgElstatsCountMin.push_back(flrStatsInt);
	}
	avgMeanWaitTime.fill(0.0);
	avgMeanWaitTime.at(0) = MAX_TIME;
	avg60SecCount.fill(0.0);
	avg60SecCount.at(0) = MAX_TIME;
	avgPercentile.fill(0.0);
	avgPercentile.at(0) = MAX_TIME;
}

StatPack::~StatPack(void)
{
}

bool StatPack::init(int runID)
{
	runNum = runID;
	// Clear the passenger counts
	for (auto &el: ElstatsCount)
	{
		el.fill(0);
	}
	// Clear the floor wait times
	for (auto &el: ElstatsWait)
	{
		el.fill(0.0);
	}
	// Clear the floor minimum wait times
	for (auto &el: ElstatsWaitMin)
	{
		el.fill(MAX_TIME);
	}
	// Clear the floor maximum wait times
	for (auto &el: ElstatsWaitMax)
	{
		el.fill(0);
	}
	// Clear the list of wait times
	waitList = priority_queue <int>();
	// Open the data file for this run if user has requested it
	if (writeFlag)
	{
		if ( runDataFile.is_open() )
		{
			runDataFile.close();
		}		
		stringstream ss;
		ss << runNum;
		string fName = baseFileName + "R" + ss.str() + ".txt";
		runDataFile.open(fName,ios::out | ios::trunc);
		if ( runDataFile.is_open() )
		{
			runDataFile << "ID,Arrival Time,Service Time,Exit Time,Elev ID,Start Floor,Destination Floor,Wait Time" << endl;
		}
		else
		{
			cout << "File did not open" << endl;
		}		
	}
	return true;
}

bool StatPack::processDataPoint(Ticket * ticket)
{
	int elevator = ticket->getElevator();
	int destination = ticket->getDestination();
	int waitTime = ticket->getService() - ticket->getArrival();
	// increment elevator passenger delivered count
	ElstatsCount.at(elevator).at(destination)++;
	// Add wait time to wait time for this elevator at this destination.
	ElstatsWait.at(elevator).at(destination) += waitTime;
	// store wait time in minWaitList if it is lower than present 
	if (waitTime < ElstatsWaitMin.at(elevator).at(destination)) ElstatsWaitMin.at(elevator).at(destination) = waitTime;
	// store wait time in maxWaitList if it is higher than present number
	if (waitTime > ElstatsWaitMax.at(elevator).at(destination)) ElstatsWaitMax.at(elevator).at(destination) = waitTime;
	// store wait time in wait list
	waitList.emplace(waitTime);
	// write data if flag set
	if (waitTime < runMinWait ) runMinWait = waitTime;
	if (waitTime > runMaxWait ) runMaxWait = waitTime;
	if (writeFlag)
	{
		runDataFile <<ticket->getID() << "," << ticket->getArrival() << "," << ticket->getService() << "," << ticket->getExitTime() << "," << ticket->getElevator() << "," << ticket->getStartFloor() << "," << ticket->getDestination() << "," << ticket->getService() - ticket->getArrival() << endl;
	}
	return true;
}


bool StatPack::CalcStats()
{
	if ( runDataFile.is_open() )
	{
		runDataFile.close();
	}
	// Calculate stats, including the combined stats
	// Calculate average wait for the individual floors by elevator
	for (int i = 0; i < eCount; i++)
	{
		for (int j = 0; j < FLOOR_COUNT; j++)
		{
			ElstatsWait.at(i).at(j) /= ElstatsCount.at(i).at(j);
		}
	}
	// Calculate the mean wait time, 95Percentile wait time, and the target time wait time
	bool targetWTFlag = true;
	float waitListSize = (float) waitList.size();
	while (!waitList.empty())
	{
		if (waitList.size() == the95PercentileNumber) percentileWaitTime = waitList.top();
		if (targetWTFlag && waitList.top() <= TARGET_MAX_WAIT_TIME)
		{
			the60SecCount = waitList.size();
			targetWTFlag = false;
		}
		meanWaitTime += waitList.top();
		waitList.pop();
	}
	meanWaitTime /= waitListSize;
	//TODO add code to check and--if required--write out the calculated stats
	if (writeFlag)
	{
		stringstream ss;
		ss << runNum;
		string fName = baseFileName + "_Stats" + "_" + ss.str() + ".txt";
		runDataFile.open(fName,ios::out | ios::trunc);
		if (!runDataFile.is_open() )
		{
			cout << "Cannot open Stats File." << endl;
		}
		else
		{
			runDataFile << "Wait Times" << endl;
			runDataFile << "Elevator,Min,Flr 0,Max,Min,Flr 1,Max,Min,Flr 2,Max,Min,Flr 3,Max,Min,Flr 4,Max,Totals" << endl;
			for (int i = 0; i < eCount; i++)
			{
				runDataFile << i << ",";
				for (int j = 0; j < FLOOR_COUNT; j++)
				{
					runDataFile << ElstatsWaitMin.at(i).at(j) << ","<< ElstatsWait.at(i).at(j) << ","<< ElstatsWaitMax.at(i).at(j) << ",";
				}
				runDataFile << endl;
			}
			runDataFile << endl << "Counts" << endl;
			runDataFile << "Elevator,Flr 0,Flr 1,Flr 2,Flr 3,Flr 4,Totals" << endl;
			for (int i = 0; i < eCount; i++)
			{
				runDataFile << i << ",";
				for (int j = 0; j < FLOOR_COUNT; j++)
				{
					runDataFile << ElstatsCount.at(i).at(j) << ",";
				}
				runDataFile << endl;
			}
			runDataFile << endl << "Overall Wait Times" << endl;
			runDataFile << "Mean" << "," << meanWaitTime << endl;
			runDataFile << "60 sec %" << "," << 100*the60SecCount/TOTAL_TICKETS << endl;
			runDataFile << "95%" << "," << percentileWaitTime << endl;
			runDataFile << "Minimum" << "," << runMinWait << endl;
			runDataFile << "Maximum" << "," << runMaxWait << endl;
			runDataFile.close();
		}

	}		
	// Accumulation combined stats
	// Accumulate Wait times per elevator per floor
	for (int i = 0; i < eCount; i++)
	{
		for (int j = 0; j < FLOOR_COUNT; j++)
		{
			if (ElstatsWaitMin.at(i).at(j) < avgElstatsWaitMin.at(i).at(j) ) avgElstatsWaitMin.at(i).at(j) = ElstatsWaitMin.at(i).at(j);
			avgElstatsWait.at(i).at(j) += ElstatsWait.at(i).at(j);
			if (ElstatsWaitMax.at(i).at(j) > avgElstatsWaitMax.at(i).at(j) ) avgElstatsWaitMax.at(i).at(j) = ElstatsWaitMax.at(i).at(j);
		}
	}
	// Accumulate Counts per elevator per floor
	for (int i = 0; i < eCount; i++)
	{
		for (int j = 0; j < FLOOR_COUNT; j++)
		{
			if (ElstatsCount.at(i).at(j) < avgElstatsCountMin.at(i).at(j) ) avgElstatsCountMin.at(i).at(j) = ElstatsCount.at(i).at(j);
			avgElstatsCount.at(i).at(j) += ElstatsCount.at(i).at(j);
			if (ElstatsCount.at(i).at(j) > avgElstatsCountMax.at(i).at(j) ) avgElstatsCountMax.at(i).at(j) = ElstatsCount.at(i).at(j);
		}
	}
	// Accumulate summary stats
	// Summary wait times--minimum, average, and maximum
	if (runMinWait < avgMeanWaitTime.at(0)) avgMeanWaitTime.at(0) = runMinWait;
	avgMeanWaitTime.at(1) += meanWaitTime;
	if (runMaxWait > avgMeanWaitTime.at(2)) avgMeanWaitTime.at(2) = runMaxWait;
	// Summary 60 second count number--minimum, aveage, and maximum
	if (the60SecCount < avg60SecCount.at(0)) avg60SecCount.at(0) = the60SecCount;
	avg60SecCount.at(1) += the60SecCount;
	if (the60SecCount > avg60SecCount.at(2)) avg60SecCount.at(2) = the60SecCount;
	// Summary 95 percentile values--minimum, average, and maximum
	if (percentileWaitTime < avgPercentile.at(0)) avgPercentile.at(0) = percentileWaitTime;
	avgPercentile.at(1) += percentileWaitTime;
	if (percentileWaitTime > avgPercentile.at(2)) avgPercentile.at(2) = percentileWaitTime;

	return true;
}


bool StatPack::writeSimData(void)
{
	// Adjust averages
	runNum++;
	avgMeanWaitTime.at(1) /= runNum;
	avg60SecCount.at(1) /= runNum;
	for (auto &c: avg60SecCount)
	{
		c /= TOTAL_TICKETS; // convert from rank to ratio
		c *= 100.0;			// convert from ratio to percent
	}
	avgPercentile.at(1) /= runNum;
	// Write out the combined stats
	if ( runDataFile.is_open() )
	{
		runDataFile.close();
	}
	stringstream ss;
	ss << runNum;
	string fName = baseFileName + "_Combined_Stats.txt";
	runDataFile.open(fName,ios::out | ios::trunc);
	if (!runDataFile.is_open() )
	{
		cout << "Cannot open Stats File." << endl;
	}
	else
	{
		runDataFile << "Wait Times" << endl;
		runDataFile << "Elevator,Min,Flr 0,Max,Min,Flr 1,Max,Min,Flr 2,Max,Min,Flr 3,Max,Min,Flr 4,Max,Totals" << endl;
		for (int i = 0; i < eCount; i++)
		{
			runDataFile << i ;
			for (int j = 0; j < FLOOR_COUNT; j++)
			{
				runDataFile << "," << ElstatsWaitMin.at(i).at(j) << ","<< ElstatsWait.at(i).at(j) << ","<< ElstatsWaitMax.at(i).at(j);
			}
			runDataFile << endl;
		}
		runDataFile << endl << "Counts" << endl;
		runDataFile << "Elevator,Min,Flr 0,Max,Min,Flr 1,Max,Min,Flr 2,Max,Min,Flr 3,Max,Min,Flr 4,Max,Totals" << endl;
		for (int i = 0; i < eCount; i++)
		{
			runDataFile << i;
			for (int j = 0; j < FLOOR_COUNT; j++)
			{
				runDataFile << "," << avgElstatsCountMin.at(i).at(j) << "," << ElstatsCount.at(i).at(j) << "," << avgElstatsCountMax.at(i).at(j);
			}
			runDataFile << endl;
		}
		runDataFile << endl << "Overall Wait Times" << "Min" << "Avg" << "Max" << endl;
		runDataFile << "Mean";
		for (auto t: avgMeanWaitTime)
		{
			runDataFile << "," << t;
		}
		runDataFile << endl;
		runDataFile << "60 sec %";
		for (auto c: avg60SecCount)
		{
			runDataFile << "," << c;
		}
		runDataFile << endl;
		runDataFile << "95%";
		for (auto p: avgPercentile)
		{
			runDataFile << "," << p;
		}
		runDataFile << endl;		runDataFile.close();
	}
	return true;
}
